import 'dart:async';
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:jump_scape/js_data/js_http/js_http.dart';
import 'package:language_code/language_code.dart';
import 'package:uuid/uuid.dart';

import '../js_config.dart';
import '../objectbox.g.dart';
import 'js_box/js_init.dart';

class JsEventName {
  static const jsNameLaunch = 'launch'; // 启动页面埋点

  // 2. 登录页面埋点
  static const jsNameQuicklogin = 'quicklogin'; // 快速登录
  static const jsNameApplelogin = 'applelogin'; // 苹果登录
  static const jsNameTerms = 'terms'; // 用户协议
  static const jsNamePrivacy = 'privacy'; // 隐私协议
  static const jsNameLoginsuccess = 'loginsuccess'; // 登录成功

  // 各主tab点击事件埋点
  static const jsNameHome = "home";
  static const jsNameRecordSpring = "recordSpring";
  static const jsNameRecordWinter = "recordWinter";
  static const jsNameRecordCity = "recordCity";
  static const jsNameMine = "mine";
  static const jsNameMineEdit = "mineEdit";
  static const jsNameAbout = "about";
  static const jsNameSetting = "setting";
  static const jsNameTopic = "topic";
  static const jsNameGame = "game";

  // 内购埋点
  static const jsNamePurchase = 'purchase'; // 打开充值页面
  static const jsNameCreateorder = 'createorder'; // 创建内购订单

  // 我的埋点
  static const jsNameEditavatar = 'editavatar'; // 编辑头像
  static const jsNameLogout = 'logout'; // 退出登录
  static const jsNameDeleteaccount = 'deleteaccount'; // 删除账号
}

class JsEventManager {
  JsEventManager._();

  static const String _eventKey = 'event';
  static const String _tmKey = 'tm';
  static const String _pageKey = 'page';
  static const String _subPageKey = 'subPage';
  static const String _terPageKey = 'terPage';
  static const String _broadcasterIdKey = 'broadcasterId';
  static const String _durationKey = 'duration';

  static void statPage({
    String? page,
    String? subPage,
    String? terPage,
    String? broadcasterId,
  }) {
    var map = <String, dynamic>{};
    // ⚠️page传入下方的埋点事件page值
    if (page != null) {
      map[_pageKey] = page;
    }
    if (subPage != null) {
      map[_subPageKey] = subPage;
    }
    if (terPage != null) {
      map[_terPageKey] = terPage;
    }
    if (broadcasterId != null) {
      map[_broadcasterIdKey] = broadcasterId;
    }
    map[_durationKey] = "0";
    map[_eventKey] = 'pv';
    map[_tmKey] = DateTime.now().millisecondsSinceEpoch.toString();

    debugPrint("[Event] 😄 $map");

    _JsEventManager().putLog(_JsEventType.globalBehavior, map);
  }
}

enum _JsEventType {
  globalBehavior('event', 'global_behavior', 'event'),
  clickEvent("event", "click", "event");

  final String logType;
  final String subType;
  final String behavior;

  const _JsEventType(this.logType, this.subType, this.behavior);
}

class _JsEventManager {
  static final _JsEventManager _instance = _JsEventManager._internal();
  factory _JsEventManager() {
    return _instance;
  }
  _JsEventManager._internal();

  static const String tag = "JsEventManager"; // 日志标签
  static const int limitSize = 13; // 打点任务最多缓存长度
  static int _serialId = 0;
  // 每次启动App该值会修改
  static String launcherId = md('${DateTime.now().millisecondsSinceEpoch}${const Uuid().v4()}');
  final List<Map<String, dynamic>> cacheList = [];
  bool _isWaitingForSend = false; // 是否需要延迟上报

  /// 上报日志
  void putLog(_JsEventType logType, Map<String, dynamic>? event) async {
    if (logType.logType.isEmpty || logType.subType.isEmpty || logType.behavior.isEmpty || event == null || event.isEmpty) {
      return;
    }
    Map<String, dynamic> itemData = await _buildItemData(logType.logType, logType.subType, logType.behavior, event);
    cacheList.insert(0, itemData);
    sendDelayed();
  }

  /// 延时发送日志
  void sendDelayed() {
    if (_isWaitingForSend) {
      return;
    }
    _isWaitingForSend = true;
    Timer(const Duration(milliseconds: 2000), () {
      _isWaitingForSend = false;
      sendLog(false);
    });
  }

  /// 发日志打点
  void sendLog(bool forceUploadAll) {
    try {
      List<Map<String, dynamic>> sendList = [];
      if (cacheList.isEmpty) {
        return;
      } else if (cacheList.length >= limitSize && !forceUploadAll) {
        sendList.addAll(cacheList.sublist(0, limitSize));
        cacheList.removeRange(0, limitSize);
      } else {
        sendList.addAll(cacheList);
        cacheList.clear();
      }
      _doUpload(sendList, true);
    } catch (e) {
      e.printInfo();
    }
  }

  /// 立马发送日志
  void putLogRightNow(_JsEventType logType, Map<String, dynamic>? event) async {
    if (logType.logType.isEmpty || logType.subType.isEmpty || logType.behavior.isEmpty || event == null || event.isEmpty) {
      return;
    }
    Map<String, dynamic> itemData = await _buildItemData(logType.logType, logType.subType, logType.behavior, event);
    List<Map<String, dynamic>> list = [];
    list.add(itemData);

    _doUpload(list, false);
  }

  /// 执行上传
  Future<void> _doUpload(List<Map<String, dynamic>> sendList, bool isContinue) async {
    final data = await JsHttp.event(sendList);
    debugPrint("[Event] ⌚️ $data");
    if (isContinue) {
      sendDelayed();
    }
  }

  Future<Map<String, dynamic>> _buildItemData(String logType, String subType, String behavior, Map<String, dynamic>? event) async {
    String platform = JsApp.getPlatform();
    String platformVer = await JsApp.getPlatformVer();
    final model = await JsApp.getDeviceModel();
    final setting = await JsObjectBox.setting;
    final deviceId = setting.deviceId;
    final userId = setting.userId;
    final pkg = await JsApp.getPackageName();
    final ver = await JsApp.getVersionName();
    final pVer = await JsApp.getBuildNumber();
    final lang = LanguageCode.code.code;
    final userModel = JsObjectBox.memberBox.query(MemberBox_.userId.equals(userId)).build().findFirst();
    _serialId++;

    Map<String, dynamic> itemData = {};
    itemData[_JsEventKey.jsKeyData] = [event];
    itemData[_JsEventKey.jsKeyLogType] = 'event';
    itemData[_JsEventKey.jsKeySubtype] = 'global_behavior';
    itemData[_JsEventKey.jsKeyBehavior] = 'event';
    itemData[_JsEventKey.jsKeyDeviceId] = deviceId;
    itemData[_JsEventKey.jsKeyAndroidId] = deviceId;
    itemData[_JsEventKey.jsKeyUserId] = userId;
    itemData[_JsEventKey.jsKeyPkg] = pkg;
    itemData[_JsEventKey.jsKeyChnId] = '';
    itemData[_JsEventKey.jsKeyUtmSource] = '';
    itemData[_JsEventKey.jsKeyVer] = ver;
    itemData[_JsEventKey.jsKeyPlatform] = platform;
    itemData[_JsEventKey.jsKeyPlatformVer] = platformVer;
    itemData[_JsEventKey.jsKeyModel] = model;
    itemData[_JsEventKey.jsKeyPVer] = pVer;
    itemData[_JsEventKey.jsKeyLanId] = launcherId;
    itemData[_JsEventKey.jsKeySecId] = _serialId;
    itemData[_JsEventKey.jsKeySysLan] = lang;
    itemData[_JsEventKey.jsKeyCountry] = userModel?.country ?? '';
    itemData[_JsEventKey.jsKeyIsInBg] = false;
    itemData[_JsEventKey.jsKeyIsAnchor] = false;
    itemData[_JsEventKey.jsKeyClientType] = "common";
    itemData[_JsEventKey.jsKeyBizver] = '';
    itemData["tm"] = DateTime.now().millisecondsSinceEpoch.toString();
    return itemData;
  }

  static String md(String input) => md5.convert(utf8.encode(input)).toString();
}

class _JsEventKey {
  static const String jsKeyData = "data";
  static const String jsKeyLogType = "log_type";
  static const String jsKeySubtype = "subtype";
  static const String jsKeyBehavior = "behavior";
  static const String jsKeyDeviceId = "device-id";
  static const String jsKeyAndroidId = "android_id";
  static const String jsKeyUserId = "user_id";
  static const String jsKeyPkg = "pkg";
  static const String jsKeyChnId = "chn_id";
  static const String jsKeyVer = "ver";
  static const String jsKeyPlatform = "platform";
  static const String jsKeyPlatformVer = "platform_ver";
  static const String jsKeyModel = "model";
  static const String jsKeyPVer = "p_ver";
  static const String jsKeyLanId = "lan_id";
  static const String jsKeySecId = "sec_id";
  static const String jsKeyUtmSource = "utm_source";
  static const String jsKeyAfAdgroupId = "af_adgroup_id";
  static const String jsKeyAfAdset = "af_adset";
  static const String jsKeyAfAdsetId = "af_adset_id";
  static const String jsKeyAfStatus = "af_status";
  static const String jsKeyAfAgency = "af_agency";
  static const String jsKeyAfChannel = "af_channel";
  static const String jsKeyAfCampaign = "campaign";
  static const String jsKeyAfCampaignId = "campaign_id";
  static const String jsKeyCountry = "country";
  static const String jsKeySysLan = "sys_lan";
  static const String jsKeyIsInBg = "is_in_bg";
  static const String jsKeyIsAnchor = "is_anchor";
  static const String jsKeyClientType = "client_type";
  static const String jsKeyBizver = "bizver";
}
